home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / telecomm / sticpsrc.lzh / SOURCE.ARC / TNC2.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-04-28  |  34.6 KB  |  1,366 lines

  1. /* TNC-2 emulator for use by RLI or MBL bbs (or other programs) */
  2.  
  3. #include <ctype.h>
  4. #include <time.h>
  5. #include "global.h"
  6. #include "mbuf.h"
  7. #include "iface.h"
  8. #include "timer.h"
  9. #include "ax25.h"
  10. #include "lapb.h"
  11. #include "kiss.h"
  12. #include "slip.h"
  13. #include "tnc2.h"
  14.  
  15. #undef    ALLCMDS        1        /* include unsupported cmds? */
  16. #define BEACON        1        /* include BEACON possibility? */
  17.  
  18. struct tnc *tnc2s = NULLTNC;        /* TNC controlblocks */
  19. static void tnc2_recv();        /* upcall handlers */
  20.  
  21. /* TNC commands supported by the emulator */
  22. static void dotnconmode(),dotnconnect(),dotnconverse(),
  23.     dotncstatus(),dotndaytime(),dotndiscon(),dotndisplay(),
  24.     dotnmycall(),dotnrestart(),dotntrans(),dotnunproto();
  25.  
  26. void dotnmheard(),dotnreset();
  27.  
  28. static struct tnccommand tnccommands[] =
  29. {
  30.     "8BITCONV", TC_ON_OFF,    0,    NULLVFP,
  31. #ifdef ALLCMDS
  32.     "AX25L2V2", TC_ON_OFF,    1,    NULLVFP,    /* not used */
  33. #endif
  34.     "AUTOLF",    TC_ON_OFF,    2,    NULLVFP,
  35. #ifdef ALLCMDS
  36.     "AWLEN",    TC_DECVAL,    3,    NULLVFP,    /* not used */
  37.     "AXDELAY",    TC_DECVAL,    4,    NULLVFP,    /* not used */
  38.     "AXHANG",    TC_DECVAL,    5,    NULLVFP,    /* not used */
  39. #endif
  40. #if (defined(BEACON) || defined(ALLCMDS))
  41.     "BEACON",    TC_TIMER,    0,    (void (*)()) 10000,
  42. #endif
  43.     "BBSMSGS",    TC_ON_OFF,    55,    NULLVFP,
  44.     "BKONDEL",    TC_ON_OFF,    6,    NULLVFP,
  45. #if (defined(BEACON) || defined(ALLCMDS))
  46.     "BTEXT",    TC_TEXT,    0,    NULLVFP,
  47. #endif
  48. #ifdef ALLCMDS
  49.     "BUDLIST",    TC_ON_OFF,    7,    NULLVFP,    /* not used */
  50. #endif
  51.     "CONNECT",    TC_EXEC,    0,    dotnconnect,
  52.     "CBELL",    TC_ON_OFF,    8,    NULLVFP,
  53. #ifdef ALLCMDS
  54.     "CONPERM",    TC_ON_OFF,    9,    NULLVFP,    /* not used */
  55.     "CHECK",    TC_DECVAL,    10,    NULLVFP,    /* not used */
  56.     "CLKADJ",    TC_DECVAL,    11,    NULLVFP,    /* not used */
  57. #endif
  58.     "CMDTIME",    TC_DECVAL,    12,    NULLVFP,
  59.     "CMSG",    TC_ON_OFF,    13,    NULLVFP,
  60.     "CMSGDISC", TC_ON_OFF,    14,    NULLVFP,
  61.     "CPACTIME", TC_ON_OFF,    15,    NULLVFP,
  62.     "CR",    TC_ON_OFF,    16,    NULLVFP,
  63.     "CSTATUS",    TC_EXEC,    0,    dotncstatus,
  64.     "CTEXT",    TC_TEXT,    1,    NULLVFP,
  65.     "CANLINE",    TC_HEXVAL,    17,    NULLVFP,
  66.     "COMMAND",    TC_HEXVAL,    18,    NULLVFP,
  67. #ifdef ALLCMDS
  68.     "CALSET",    TC_DECVAL,    19,    NULLVFP,    /* not used */
  69. #endif
  70.     "CANPAC",    TC_HEXVAL,    20,    NULLVFP,
  71.     "CONOK",    TC_ON_OFF,    21,    NULLVFP,
  72.     "CONMODE",    TC_MISC,    0,    dotnconmode,
  73.     "CONSTAMP", TC_ON_OFF,    22,    NULLVFP,
  74.     "CONVERSE", TC_EXEC,    0,    dotnconverse,
  75.     "DISCONNE", TC_EXEC,    0,    dotndiscon,
  76.     "DAYTIME",    TC_EXEC,    0,    dotndaytime,
  77.     "DAYUSA",    TC_ON_OFF,    23,    NULLVFP,
  78.     "DELETE",    TC_ON_OFF,    24,    NULLVFP,
  79. #ifdef ALLCMDS
  80.     "DWAIT",    TC_DECVAL,    25,    NULLVFP,    /* not used */
  81.     "DIGIPEAT", TC_ON_OFF,    26,    NULLVFP,    /* not used */
  82. #endif
  83.     "DISPLAY",    TC_EXEC,    0,    dotndisplay,
  84.     "ECHO",    TC_ON_OFF,    27,    NULLVFP,
  85. #ifdef ALLCMDS
  86.     "ESCAPE",    TC_ON_OFF,    28,    NULLVFP,    /* not used */
  87. #endif
  88.     "FLOW",    TC_ON_OFF,    29,    NULLVFP,
  89. #ifdef ALLCMDS
  90.     "FRACK",    TC_DECVAL,    30,    NULLVFP,    /* not used */
  91.     "FULLDUP",    TC_ON_OFF,    31,    NULLVFP,    /* not used */
  92.     "HEADERLN", TC_ON_OFF,    32,    NULLVFP,    /* not used */
  93.     "HEALLED",    TC_ON_OFF,    33,    NULLVFP,    /* not used */
  94.     "HID",    TC_ON_OFF,    34,    NULLVFP,    /* not used */
  95.     "ID",    TC_EXEC,    0,    NULLVFP,
  96. #endif
  97.     "K",    TC_EXEC,    0,    dotnconverse,    /* ALIAS for CONVERSE */
  98.     "KISS",    TC_ON_OFF,    76,    NULLVFP,
  99.     "KISSRX",    TC_DECVAL,    77,    NULLVFP,
  100. #ifdef ALLCMDS
  101.     "LCOK",    TC_ON_OFF,    35,    NULLVFP,    /* not used */
  102. #endif
  103.     "LFADD",    TC_ON_OFF,    36,    NULLVFP,
  104.     "LFIGNORE", TC_ON_OFF,    37,    NULLVFP,
  105. #ifdef ALLCMDS
  106.     "LCALLS",    TC_TEXT,    2,    NULLVFP,    /* not used */
  107.     "LCSTREAM", TC_ON_OFF,    38,    NULLVFP,    /* not used */
  108. #endif
  109.     "MONITOR",    TC_ON_OFF,    39,    NULLVFP,    /* not used, incl for BBS */
  110. #ifdef ALLCMDS
  111.     "MALL",    TC_ON_OFF,    40,    NULLVFP,    /* not used */
  112.     "MCON",    TC_ON_OFF,    41,    NULLVFP,    /* not used */
  113.     "MFILTER",    TC_MISC,    1,    NULLVFP,    /* not used */
  114. #endif
  115.     "MHEARD",    TC_EXEC,    0,    dotnmheard,
  116. #ifdef ALLCMDS
  117.     "MHCLEAR",    TC_EXEC,    0,    NULLVFP,
  118.     "MRPT",    TC_ON_OFF,    42,    NULLVFP,    /* not used */
  119.     "MSTAMP",    TC_ON_OFF,    43,    NULLVFP,    /* not used */
  120. #endif
  121.     "MYCALL",    TC_EXEC,    0,    dotnmycall,
  122. #ifdef ALLCMDS
  123.     "MYALIAS",    TC_TEXT,    3,    NULLVFP,    /* not used */
  124.     "MAXFRAME", TC_DECVAL,    44,    NULLVFP,    /* not used */
  125.     "MCOM",    TC_ON_OFF,    45,    NULLVFP,    /* not used */
  126. #endif
  127.     "NEWMODE",    TC_ON_OFF,    46,    NULLVFP,
  128.     "NOMODE",    TC_ON_OFF,    47,    NULLVFP,
  129. #ifdef ALLCMDS
  130.     "NUCR",    TC_ON_OFF,    48,    NULLVFP,    /* not used */
  131.     "NULF",    TC_ON_OFF,    49,    NULLVFP,    /* not used */
  132.     "NULLS",    TC_DECVAL,    50,    NULLVFP,    /* not used */
  133. #endif
  134.     "PACLEN",    TC_DECVAL,    51,    NULLVFP,
  135. #ifdef ALLCMDS
  136.     "PARITY",    TC_DECVAL,    52,    NULLVFP,    /* not used */
  137. #endif
  138.     "PASS",    TC_HEXVAL,    53,    NULLVFP,
  139. #ifdef ALLCMDS
  140.     "PASSALL",    TC_ON_OFF,    54,    NULLVFP,    /* not used */
  141. #endif
  142.     "PACTIME",    TC_TIMER,    1,    (void (*)()) 100,
  143. #ifdef ALLCMDS
  144.     "RETRY",    TC_DECVAL,    56,    NULLVFP,    /* not used */
  145. #endif
  146.     "REDISPLA", TC_HEXVAL,    57,    NULLVFP,
  147. #ifdef ALLCMDS
  148.     "RECONNEC", TC_EXEC,    0,    NULLVFP,
  149.     "RESPTIME", TC_DECVAL,    58,    NULLVFP,    /* not used */
  150. #endif
  151.     "RESTART",    TC_EXEC,    0,    dotnrestart,
  152.     "RESET",    TC_EXEC,    0,    dotnreset,
  153.     "RXBLOCK",    TC_ON_OFF,    59,    NULLVFP,
  154. #ifdef ALLCMDS
  155.     "SCREENLN", TC_DECVAL,    60,    NULLVFP,    /* not used */
  156. #endif
  157.     "SENDPAC",    TC_HEXVAL,    61,    NULLVFP,
  158.     "START",    TC_HEXVAL,    62,    NULLVFP,
  159.     "STOP",    TC_HEXVAL,    63,    NULLVFP,
  160. #ifdef ALLCMDS
  161.     "STREAMSW", TC_HEXVAL,    64,    NULLVFP,    /* not used */
  162.     "STREAMCA", TC_ON_OFF,    65,    NULLVFP,    /* not used */
  163.     "STREAMDB", TC_ON_OFF,    66,    NULLVFP,    /* not used */
  164. #endif
  165.     "TRANS",    TC_EXEC,    0,    dotntrans,
  166. #ifdef ALLCMDS
  167.     "TRIES",    TC_EXEC,    0,    NULLVFP,    /* not used */
  168. #endif
  169.     "TRFLOW",    TC_ON_OFF,    67,    NULLVFP,
  170. #ifdef ALLCMDS
  171.     "TRACE",    TC_ON_OFF,    68,    NULLVFP,    /* not used */
  172.     "TXDELAY",    TC_DECVAL,    69,    NULLVFP,    /* not used */
  173. #endif
  174.     "TXFLOW",    TC_ON_OFF,    70,    NULLVFP,
  175.     "TXUIFRAM", TC_ON_OFF,    78,    NULLVFP,
  176.     "UNPROTO",    TC_EXEC,    0,    dotnunproto,
  177. #ifdef ALLCMDS
  178.     "USERS",    TC_DECVAL,    71,    NULLVFP,    /* not used */
  179. #endif
  180.     "XFLOW",    TC_ON_OFF,    72,    NULLVFP,
  181. #ifdef ALLCMDS
  182.     "XMITOK",    TC_ON_OFF,    73,    NULLVFP,    /* not used */
  183. #endif
  184.     "XOFF",    TC_HEXVAL,    74,    NULLVFP,
  185.     "XON",    TC_HEXVAL,    75,    NULLVFP,
  186.     "",        0,        0,    NULLVFP
  187. };
  188.  
  189. static char def_params[] = {
  190.     0, 1, 1, 7, 0, 0, 1, 0, 0, 0, 30, 0, 1, 0, 0, 0,
  191.     1, 0x18, 0x03, 0, 0x19, /*1*/ 0, 0, 1, 0, 16, 1, 1, 0, 1, 3, 0,
  192.     0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 2, 1, 0, 0,
  193.     0, 0, 0, 128, 3, 0x16, 0, 0, 10, 0x12, 0, 0, 80, 0x0d, 0x11, 0x13,
  194.     0x7c, 0, 0, 0, 0, 30, 0, 1, 1, 1, 0x13, 0x11, 0, 1, 0
  195. };
  196.  
  197. char tnc2_prompt[] = "cmd:";
  198. static char tnc2_bad[] = "?bad\r";
  199. static char tnc2_call[] = "?call\r";
  200. static char tnc2_notenough[] = "?not enough\r";
  201. static char tnc2_toomany[] = "?too many\r";
  202. static char tnc2_was[] = "was ";
  203. static char tnc2_conrq[] = "*** connect request:";
  204. static char tnc2_bslcr[] = "\\\r";
  205. static char tnc2_cr[] = "\r";
  206. extern char *ax25mesgs[];
  207.  
  208. /* send message to client (BBS) */
  209. void
  210. tnc2_mesg (tnc2,s)
  211. register struct tnc *tnc2;
  212. register unsigned char *s;
  213.  
  214. {
  215.     while (*s) {
  216.     if (!tnc2_ochar(tnc2,*s))
  217.         break;
  218.  
  219.     if (*s++ == '\r' && tnc2->param[2])    /* AUTOLF */
  220.         if (!tnc2_ochar(tnc2,'\n'))
  221.         break;
  222.     }
  223. }
  224.  
  225. /* process character input in cooked mode, return 1 if line complete */
  226. int
  227. tnc2_line (tnc2,c)
  228. register struct tnc *tnc2;
  229. register char c;
  230.  
  231. {
  232.     if (tnc2->passc) {            /* prev was PASS char? */
  233.         tnc2->passc = 0;            /* clear the flag */
  234.         tnc2->linebuf[tnc2->linepos++] = c; /* store the passed char */
  235.         tnc2->linebuf[tnc2->linepos] = '\0';
  236.  
  237.         if (tnc2->param[27])        /* ECHO ON */
  238.         tnc2_mesg(tnc2,tnc2->linebuf + tnc2->linepos - 1); /* echo it */
  239.  
  240.         goto checkbuf;
  241.     }
  242.  
  243.     if (tnc2->param[17] && c == tnc2->param[17]) { /* CANLINE character */
  244.         while (tnc2->linepos > 0)
  245.         if (tnc2->linebuf[--(tnc2->linepos)] == '\r') {/* up to CR */
  246.             tnc2->linepos++;
  247.             break;
  248.         }
  249.  
  250.         tnc2_mesg(tnc2,tnc2_bslcr);
  251.         return TL_CANCEL;
  252.     }
  253.  
  254.     if (tnc2->param[18] && c == tnc2->param[18]) /* COMMAND character */
  255.         return TL_COMMAND;
  256.  
  257.     if (tnc2->param[20] && c == tnc2->param[20]) { /* CANPAC character */
  258.         tnc2->linepos = 0;            /* clear entire line (packet) */
  259.         tnc2_mesg(tnc2,tnc2_bslcr);
  260.         return TL_CANCEL;
  261.     }
  262.  
  263.     if ((tnc2->param[24] && c == 0x7f) ||    /* DELETE ON, DEL */
  264.         (!tnc2->param[24] && c == '\b')) {    /* DELETE OFF, BS */
  265.         if (tnc2->linepos > 0) {
  266.         tnc2->linepos--;
  267.         if (tnc2->param[27])        /* ECHO ON */
  268.             tnc2_mesg(tnc2,tnc2->param[6]? /* BKONDEL */
  269.                     "\b \b" : "\\");
  270.         }
  271.         return TL_INCOMPLETE;
  272.     }
  273.  
  274.     if (tnc2->param[37] && c == '\n')    /* LFIGNORE */
  275.         return TL_INCOMPLETE;
  276.  
  277.     if (tnc2->param[53] && c == tnc2->param[53]) { /* PASS character */
  278.         tnc2->passc = 1;            /* set a flag */
  279.         return TL_INCOMPLETE;
  280.     }
  281.  
  282.     if (tnc2->param[57] && c == tnc2->param[57]) { /* REDISPLAY character */
  283.         tnc2_mesg(tnc2,tnc2_bslcr);
  284.  
  285.         if (tnc2->mode == TM_CMD)        /* dirty, but... */
  286.         tnc2_mesg(tnc2,tnc2_prompt);
  287.  
  288.         tnc2->linebuf[tnc2->linepos] = '\0';
  289.         tnc2_mesg(tnc2,tnc2->linebuf);
  290.  
  291.         return TL_INCOMPLETE;
  292.     }
  293.  
  294.     if (tnc2->mode != TM_CMD && c == tnc2->param[61]) { /* SENDPAC character */
  295.         if (tnc2->param[16])        /* CR ON? */
  296.         tnc2->linebuf[tnc2->linepos++] = c;
  297.  
  298.         if (tnc2->param[36] && c == '\r')    /* LFADD */
  299.         tnc2->linebuf[tnc2->linepos++] = '\n';
  300.  
  301.         tnc2->linebuf[tnc2->linepos] = '\0';
  302.  
  303.         if (tnc2->param[27] && tnc2->param[16]) /* ECHO ON, CR ON? */
  304.         tnc2_mesg(tnc2,tnc2->linebuf + tnc2->linepos - 1);
  305.  
  306.         return TL_PACKET;
  307.     }
  308.  
  309.     if ((tnc2->param[62] && c == tnc2->param[62]) || /* START */
  310.         (tnc2->param[63] && c == tnc2->param[63])) /* STOP */
  311.         return TL_INCOMPLETE;        /* ignore them */
  312.  
  313.     tnc2->linebuf[tnc2->linepos++] = c;    /* store it */
  314.     tnc2->linebuf[tnc2->linepos] = '\0';
  315.  
  316.     if (tnc2->param[27])            /* ECHO ON */
  317.         tnc2_mesg(tnc2,tnc2->linebuf + tnc2->linepos - 1); /* echo it */
  318.  
  319.     if (c == '\r') {            /* CR is end of line */
  320.         if (tnc2->mode == TM_CMD)
  321.         return TL_COMPLETE;
  322.         else
  323.         if (tnc2->param[36])        /* LFADD */
  324.             tnc2->linebuf[tnc2->linepos++] = '\n';
  325.     }
  326.  
  327. checkbuf:
  328.     if (tnc2->linepos >= sizeof(tnc2->linebuf) - 3) /* buffer full */
  329.         return TL_COMPLETE;
  330.  
  331.     return TL_INCOMPLETE;            /* await more characters */
  332. }
  333.  
  334. /* get a word from the command line and convert to uppercase */
  335. static char *
  336. getword (cpp)
  337. register char **cpp;
  338.  
  339. {
  340.     register char c;
  341.     char *rv;
  342.  
  343.     if (*cpp == NULLCHAR)            /* already at end? */
  344.     return NULLCHAR;
  345.  
  346.     while ((c = **cpp) == ' ' || c == '\t')    /* skip leading whitespace */
  347.     (*cpp)++;
  348.  
  349.     if (c == '\0' || c == '\r')            /* now at end of line? */
  350.     return NULLCHAR;
  351.  
  352.     rv = *cpp;
  353.  
  354.     while ((c = **cpp) != '\0' && c != '\r' &&    /* collect it */
  355.        c != ' ' && c != '\t' && c != ',') {
  356.     if (islower(c))
  357.         **cpp = toupper(c);
  358.  
  359.     (*cpp)++;
  360.     }
  361.  
  362.     *(*cpp)++ = '\0';                /* terminate word */
  363.  
  364.     if (c == '\0' || c == '\r')            /* now at end? */
  365.     *cpp = NULLCHAR;
  366.     else
  367.     if (c == ',')                /* skip comma */
  368.         (*cpp)++;
  369.  
  370.     return rv;
  371. }
  372.  
  373. /* return date and time in a string */
  374. char *
  375. tnc2_daytime (tnc2,tloc)
  376. struct tnc *tnc2;
  377. time_t tloc;
  378.  
  379. {
  380.     static char dt_string[20];            /* static area for result */
  381.     struct tm *ltime;
  382.  
  383.     ltime = localtime(&tloc);            /* convert time to tm_t */
  384.  
  385.     if (tnc2->param[23])            /* DAYUSA */
  386.     sprintf(dt_string,"%02d/%02d/%02d %02d:%02d:%02d",
  387.               ltime->tm_mon + 1,ltime->tm_mday,ltime->tm_year,
  388.               ltime->tm_hour,ltime->tm_min,ltime->tm_sec);
  389.     else
  390.     sprintf(dt_string,"%02d-%02d-%02d %02d:%02d:%02d",
  391.               ltime->tm_mday,ltime->tm_mon + 1,ltime->tm_year,
  392.               ltime->tm_hour,ltime->tm_min,ltime->tm_sec);
  393.  
  394.     return dt_string;
  395. }
  396.  
  397. /* show parameter value, with optional "was" */
  398. static void
  399. showparm (tnc2,tcmd,was)
  400. register struct tnc *tnc2;
  401. struct tnccommand *tcmd;
  402. char was;
  403.  
  404. {
  405.     char buf[40],*p;
  406.  
  407.     sprintf(buf,"%-9s%s",tcmd->name,was? tnc2_was : "");
  408.     p = buf + strlen(buf);
  409.  
  410.     switch (tcmd->type)
  411.     {
  412.     case TC_ON_OFF:
  413.     strcpy(p,tnc2->param[tcmd->paramnum]? "ON" : "OFF");
  414.     break;
  415.  
  416.     case TC_DECVAL:
  417.     sprintf(p,"%d",uchar(tnc2->param[tcmd->paramnum]));
  418.     break;
  419.  
  420.     case TC_HEXVAL:
  421.     sprintf(p,"$%02x",uchar(tnc2->param[tcmd->paramnum]));
  422.     while (*++p)
  423.         if (islower(*p))
  424.         *p = toupper(*p);
  425.     break;
  426.  
  427.     case TC_TEXT:
  428.     tnc2_mesg(tnc2,buf);
  429.     tnc2_mesg(tnc2,(tnc2->text[tcmd->paramnum] != NULLCHAR)?
  430.                 tnc2->text[tcmd->paramnum] : tnc2_cr);
  431.     return;
  432.  
  433.     case TC_TIMER:
  434.     sprintf(p,"%s %d",tnc2->every[tcmd->paramnum]? "EVERY" : "AFTER",
  435.         (int) (TICK2MS(tnc2->timer[tcmd->paramnum].start) / (long) tcmd->func));
  436.     break;
  437.  
  438.     case TC_MISC:
  439.     switch (tcmd->paramnum)
  440.     {
  441.     case 0:                    /* CONMODE */
  442.         strcat(p,(tnc2->conmode == TM_CONV)? "CONVERSE" : "TRANS");
  443.         break;
  444.  
  445.     case 1:                    /* MFILTER */
  446.         break;
  447.     }
  448.     }
  449.  
  450.     strcat(p,tnc2_cr);
  451.     tnc2_mesg(tnc2,buf);
  452. }
  453.  
  454. /* execute command in the linebuffer */
  455. static void
  456. tnc2_exec (tnc2)
  457. register struct tnc *tnc2;
  458.  
  459. {
  460.     register char *word;
  461.     register struct tnccommand *tcmd = tnccommands;
  462.     char *p = tnc2->linebuf;
  463.     register int paramnum;
  464.     int len,v;
  465.  
  466.     if ((word = getword(&p)) == NULLCHAR)    /* get keyword */
  467.     return;                    /* empty line, ignore it */
  468.  
  469.     if ((len = strlen(word)) == 0)        /* take keyword len */
  470.     goto eh;                /* garbage, print ?EH */
  471.  
  472.     while (tcmd->name[0] != '\0') {        /* check the command names */
  473.     if (!strncmp(tcmd->name,word,len)) {
  474.         paramnum = tcmd->paramnum;        /* have it at easy access */
  475.  
  476.         switch (tcmd->type)
  477.         {
  478.         case TC_EXEC:            /* executable command */
  479.         case TC_MISC:            /* misc setting command */
  480.         if (tcmd->func != NULLVFP)
  481.             (*tcmd->func)(tnc2,p);
  482.  
  483.         return;
  484.  
  485.         case TC_ON_OFF:            /* On/Off switch */
  486.         if ((word = getword(&p)) == NULLCHAR)
  487.             showparm(tnc2,tcmd,0);    /* show value */
  488.         else {
  489.             if (word[0] == 'Y' || (word[0] == 'O' && word[1] == 'N'))
  490.             v = 1;
  491.             else
  492.             if (word[0] == 'N' || (word[0] == 'O' && word[1] == 'F'))
  493.                 v = 0;
  494.             else {
  495.                 tnc2_mesg(tnc2,tnc2_bad);
  496.                 return;
  497.             }
  498.  
  499.             showparm(tnc2,tcmd,1);    /* show old value */
  500.             tnc2->param[paramnum] = v;    /* set new */
  501.             tnc2_flow(tnc2,2 - tnc2->param[72]); /* flowctrl based on XFLOW */
  502.         }
  503.  
  504.         return;
  505.  
  506.         case TC_DECVAL:            /* decimal value */
  507.         case TC_HEXVAL:            /* hexadecimal value */
  508.         if ((word = getword(&p)) == NULLCHAR)
  509.             showparm(tnc2,tcmd,0);    /* show value */
  510.         else {
  511.             showparm(tnc2,tcmd,1);    /* show old value */
  512.  
  513.             if (*word == '$')        /* new in HEX? */
  514.             tnc2->param[paramnum] = htoi(++word);
  515.             else
  516.             tnc2->param[paramnum] = atoi(word);
  517.         }
  518.  
  519.         return;
  520.  
  521.         case TC_TEXT:            /* a text */
  522.         if (p != NULLCHAR) {
  523.             while (*p == ' ' || *p == '\t') /* skip whitespace */
  524.             p++;
  525.  
  526.             if (*p == '\0' || *p == '\r')
  527.             p = NULLCHAR;
  528.         }
  529.  
  530.         if (p == NULLCHAR)
  531.             showparm(tnc2,tcmd,0);    /* show value */
  532.         else {
  533.             showparm(tnc2,tcmd,1);    /* show old value */
  534.  
  535.             if (tnc2->text[paramnum] != NULLCHAR)
  536.             free(tnc2->text[paramnum]);
  537.  
  538.             if ((p[0] == '%' || p[0] == '&') &&
  539.             (p[1] == '\0' || p[1] == '\r'))
  540.             tnc2->text[paramnum] = NULLCHAR;
  541.             else
  542.             if ((tnc2->text[paramnum] = malloc(strlen(p) + 1)) != NULLCHAR)
  543.                 strcpy(tnc2->text[paramnum],p);
  544.         }
  545.         return;
  546.  
  547.         case TC_TIMER:            /* Every/After timer */
  548.         if ((word = getword(&p)) == NULLCHAR)
  549.             showparm(tnc2,tcmd,0);    /* show value */
  550.         else {
  551.             if (word[0] == 'E')
  552.             v = 1;
  553.             else
  554.             if (word[0] == 'A')
  555.                 v = 0;
  556.             else {
  557.                 tnc2_mesg(tnc2,tnc2_bad);
  558.                 return;
  559.             }
  560.  
  561.             if ((word = getword(&p)) == NULLCHAR) {
  562.             tnc2_mesg(tnc2,tnc2_notenough);
  563.             return;
  564.             }
  565.  
  566.             showparm(tnc2,tcmd,1);    /* show old value */
  567.             tnc2->every[paramnum] = v;    /* set new */
  568.             stop_timer(&tnc2->timer[paramnum]);
  569.             if ((tnc2->timer[paramnum].start = MS2TICK((long) tcmd->func) * atoi(word)) != 0)
  570.             start_timer(&tnc2->timer[paramnum]);
  571.         }
  572.  
  573.         return;
  574.         }
  575.     }
  576.  
  577.     tcmd++;                    /* try to match next cmd */
  578.     }
  579.  
  580. eh:
  581.     tnc2_mesg(tnc2,"?EH\r");            /* command not found */
  582. }
  583.  
  584. /* send any data present in the linebuffer */
  585. static void
  586. tnc2_send (tnc2)
  587. register struct tnc *tnc2;
  588.  
  589. {
  590.     switch (tnc2->mode)
  591.     {
  592.     case TM_CMD:
  593.     return;                    /* command mode, don't send */
  594.  
  595.     case TM_TRANS:                /* trans mode, add cmdchars */
  596.     if (tnc2->passc) {            /* buffered cmdchars? */
  597.         if (run_timer(&tnc2->timer[2])) {    /* still timing? */
  598.         start_timer(&tnc2->timer[1]);    /* then restart PACTIME */
  599.         return;                /* and try again later */
  600.         }
  601.         while (tnc2->passc) {
  602.         tnc2->linebuf[tnc2->linepos++] = tnc2->param[18]; /* add them */
  603.         tnc2->passc--;
  604.         }
  605.     }
  606.     break;
  607.     }
  608.  
  609.     if (tnc2->linepos != 0) {
  610.     if (tnc2->axp != NULLAX25)        /* connection? */
  611.         sendp_ax25(tnc2->axp,qdata(tnc2->linebuf,tnc2->linepos),
  612.                PID_FIRST|PID_LAST|PID_NO_L3);
  613.     else
  614.         if (tnc2->param[78])        /* TXUIFRAM */
  615.         sendui_ax25(tnc2->interface,&tnc2->unproto,
  616.                 qdata(tnc2->linebuf,tnc2->linepos),
  617.                 PID_FIRST|PID_LAST|PID_NO_L3);
  618.  
  619.     tnc2->linepos = 0;            /* clear the buffer */
  620.     }
  621.  
  622.     if (tnc2->every[1] &&            /* PACTIME EVERY ... */
  623.     !run_timer(&tnc2->timer[1]) &&
  624.     (tnc2->mode == TM_TRANS || tnc2->param[15])) /* TRANS or CPACTIME */
  625.     start_timer(&tnc2->timer[1]);
  626. }
  627.  
  628. /* go to command mode */
  629. static void tnc2_cmd (tnc2)
  630. register struct tnc *tnc2;
  631.  
  632. {
  633.     tnc2_flow(tnc2,2 - tnc2->param[72]);    /* flowctrl based on XFLOW */
  634.     tnc2_mesg(tnc2,tnc2_cr);
  635.     tnc2_mesg(tnc2,tnc2_prompt);
  636.     tnc2->mode = TM_CMD;
  637.     tnc2->linepos = 0;
  638.     tnc2->timer[2].func = NULLVFP;        /* no CMDTIME func */
  639.     tnc2->passc = 0;
  640. }
  641.  
  642. /* this routine gets called by main() each loop, to process incoming data */
  643. /* for the emulated TNC2 */
  644. void
  645. tnc2_serv (tnc2)
  646. register struct tnc *tnc2;
  647.  
  648. {
  649.     int c;
  650.     struct mbuf *bp,*slip_decode();
  651.     char gotcha = 0;
  652.  
  653.     /* check if we should block input (packet queue too long) if not, */
  654.     /* read characters from client and process them               */
  655.  
  656.     while ((tnc2->mode == TM_CMD || tnc2->axp == NULLAX25 || len_q(tnc2->axp->txq) <= tnc2->axp->maxframe) &&
  657.        (c = tnc2_ichar(tnc2)) >= 0)
  658.     {
  659.     switch (tnc2->mode)
  660.     {
  661.     case TM_CMD:                /* command mode */
  662.         if (c == TCH_BREAK)            /* a BREAK received? */
  663.         break;                /* ignore it */
  664.  
  665.         switch (tnc2_line(tnc2,c & 0x7f))    /* edit an input line */
  666.         {
  667.         case TL_COMPLETE:            /* done entering command */
  668.         tnc2_exec(tnc2);        /* execute command */
  669.         tnc2->linepos = 0;        /* clear the buffer */
  670.         if (tnc2->mode != TM_CMD)    /* changed mode? */
  671.             break;
  672.  
  673.         case TL_CANCEL:            /* cancelled */
  674.         tnc2_mesg(tnc2,tnc2_prompt);    /* new prompt */
  675.         break;
  676.         }
  677.         break;
  678.  
  679.     case TM_CONV:                /* converse mode */
  680.         if (c == TCH_BREAK)            /* a BREAK received? */
  681.         c = tnc2->param[18];        /* make command-mode escape */
  682.  
  683.         if (!tnc2->param[0])        /* 8BITCONV OFF? */
  684.         c &= ~0x80;            /* strip high bit */
  685.  
  686.         switch (tnc2_line(tnc2,c))        /* edit an input line */
  687.         {
  688.         case TL_COMPLETE:            /* entered a line */
  689.         case TL_PACKET:            /* entered a packet */
  690.         tnc2_send(tnc2);        /* send it */
  691.         break;
  692.  
  693.         case TL_COMMAND:            /* to command mode */
  694.         tnc2_cmd(tnc2);
  695.         break;
  696.         }
  697.         break;
  698.  
  699.     case TM_TRANS:                /* transparent mode */
  700.         if (tnc2->param[67] &&        /* TRFLOW */
  701.         ((tnc2->param[62] && c == tnc2->param[62]) || /* START */
  702.          (tnc2->param[63] && c == tnc2->param[63]))) /* STOP */
  703.         continue;            /* ignore them */
  704.  
  705.         if (c == TCH_BREAK) {        /* a BREAK? */
  706.         tnc2_cmd(tnc2);            /* to command mode */
  707.         break;
  708.         }
  709.  
  710.         if (tnc2->param[18] &&        /* command char enabled? */
  711.         c == uchar(tnc2->param[18]) &&    /* command-mode escape? */
  712.         tnc2->passc < 3 &&        /* less than 3 before? */
  713.         /* check if (within cmdtime && already in sequence) ||
  714.                 (after cmdtime && first in sequence) */
  715.         ((run_timer(&tnc2->timer[2]) | gotcha) ^ !tnc2->passc))
  716.         {
  717.         if (++tnc2->passc == 3){    /* 3rd command-mode char? */
  718.             tnc2->timer[2].func = tnc2_cmd; /* goto cmdmode after CMDTIME */
  719.             tnc2->timer[2].arg = (char *) tnc2;
  720.         }
  721.         break;
  722.         }
  723.         else
  724.         {
  725.         while (tnc2->passc) {        /* another, add cmdmode chars */
  726.             tnc2->linebuf[tnc2->linepos++] = tnc2->param[18];
  727.             tnc2->passc--;
  728.         }
  729.         tnc2->timer[2].func = NULLVFP;
  730.         }
  731.  
  732.         tnc2->linebuf[tnc2->linepos++] = c;
  733.  
  734.         if (tnc2->linepos >= 256 || tnc2->linepos >= uchar(tnc2->param[51]))
  735.         tnc2_send(tnc2);        /* send complete packets */
  736.  
  737.         break;
  738.  
  739.     case TM_KISS:                /* KISS mode */
  740.         if (c == TCH_BREAK)            /* a BREAK? */
  741.         break;                /* ignore it */
  742.  
  743.         if ((bp = slip_decode(&tnc2->slip,c)) != NULLBUF) { /* decode it */
  744.         switch (uchar(pullchar(&bp)))
  745.         {
  746.         case KISS_DATA:
  747.             if (bp != NULLBUF) {
  748.             if(tnc2->interface->forw != NULLIF)
  749.                 (*tnc2->interface->forw->raw)(tnc2->interface->forw,bp);
  750.             else
  751.                 (*tnc2->interface->raw)(tnc2->interface,bp);
  752.  
  753.             bp = NULLBUF;
  754.             }
  755.             break;
  756.  
  757.         case KISS_SPEC:            /* TNC-specific ctrl */
  758.             tnc2->param[77] = pullchar(&bp); /* set KISSRX */
  759.             break;
  760.  
  761.         case KISS_QUIT:
  762.             tnc2->param[76] = 0;    /* KISS OFF */
  763.             tnc2->mode = TM_CMD;
  764.             dotnrestart(tnc2);
  765.             tnc2_mesg(tnc2,tnc2_prompt);
  766.             break;
  767.         }
  768.  
  769.         free_p(bp);
  770.         }
  771.         break;
  772.     }
  773.  
  774.     gotcha = 1;
  775.     }
  776.  
  777.     if (tnc2->axp != NULLAX25)            /* connection? */
  778.     tnc2_recv(tnc2->axp,0);            /* flush receive queue */
  779.  
  780.     if (gotcha) {
  781.     if (tnc2->mode == TM_TRANS) {
  782.         tnc2->timer[2].start = SEC2TICK(uchar(tnc2->param[12])); /* CMDTIME */
  783.         start_timer(&tnc2->timer[2]);
  784.     }
  785.  
  786.     if ((tnc2->linepos != 0 || tnc2->passc != 0) && /* something buffered */
  787.         (tnc2->mode == TM_TRANS ||
  788.          (tnc2->mode == TM_CONV && tnc2->param[15])) && /* CPACTIME */
  789.         (!tnc2->every[1] || !run_timer(&tnc2->timer[1])))
  790.         start_timer(&tnc2->timer[1]);    /* start PACTIME timer */
  791.     }
  792. }
  793.  
  794. /* find the tnc controlblock for a connection */
  795. static struct tnc *
  796. find_tnc (axp)
  797. register struct ax25_cb *axp;
  798.  
  799. {
  800.     register struct tnc *tnc2;
  801.     register struct tnc *rv = NULLTNC;
  802.  
  803.     for (tnc2 = tnc2s; tnc2 != NULLTNC; tnc2 = tnc2->next) {
  804.     if (tnc2->interface == axp->interface &&
  805.         tnc2->axcall == (struct ax25_call *) axp->user) {
  806.         if (tnc2->axp == axp)
  807.         return tnc2;            /* this one is connected */
  808.  
  809.         if (tnc2->axp == NULLAX25)
  810.         rv = tnc2;            /* this one is free */
  811.     }
  812.     }
  813.  
  814.     return rv;
  815. }
  816.  
  817. /* state-change upcall handler for TNC2 */
  818. void
  819. tnc2_state(axp,old,new,msg)
  820. register struct ax25_cb *axp;
  821. int old,new,msg;
  822.  
  823. {
  824.     register struct tnc *tnc2;
  825.     struct mbuf *bp;
  826.     char tmp[100];
  827.     extern void disc_ax25();
  828.  
  829.     switch (new)                /* check new status */
  830.     {
  831.     case CONNECTED:
  832.     if ((tnc2 = find_tnc(axp)) == NULLTNC) {
  833.         axp->state = DISCONNECTED;        /* refuse it, send DM */
  834.  
  835.         /* send *** connect request to TNC's that could accept the connect */
  836.         for (tnc2 = tnc2s; tnc2 != NULLTNC; tnc2 = tnc2->next) {
  837.         if (tnc2->interface == axp->interface &&
  838.             tnc2->axcall == (struct ax25_call *) axp->user &&
  839.             tnc2->mode < TM_TRANS &&
  840.             !tnc2->param[55])        /* BBSMSGS */
  841.         {
  842.             tnc2_mesg(tnc2,tnc2_conrq);
  843.             pdax25(tmp,&axp->addr);
  844.             tnc2_mesg(tnc2,tmp);
  845.             tnc2_mesg(tnc2,tnc2_cr);
  846.         }
  847.         }
  848.  
  849.         return;
  850.     }
  851.  
  852.     if (tnc2->axp == NULLAX25) {
  853.         /* new, incoming connection */
  854.         if (!tnc2->param[21]) {        /* CONOK ON? */
  855.         axp->state = DISCONNECTED;    /* no, refuse it, send DM */
  856.  
  857.         if (tnc2->mode < TM_TRANS &&    /* *** connect request when allowed */
  858.             !tnc2->param[55]) {        /* BBSMSGS */
  859.             tnc2_mesg(tnc2,tnc2_conrq);
  860.             pdax25(tmp,&axp->addr);
  861.             tnc2_mesg(tnc2,tmp);
  862.             tnc2_mesg(tnc2,tnc2_cr);
  863.         }
  864.  
  865.         return;
  866.         }
  867.  
  868.         tnc2->axp = axp;
  869.         tnc2->status |= TS_DCD;        /* set DCD on */
  870.  
  871.         if (tnc2->param[13] && tnc2->text[1] != NULLCHAR) {/* CMSG, CTEXT */
  872.         if ((bp = alloc_mbuf(1)) != NULLBUF) {
  873.             *bp->data = PID_FIRST|PID_LAST|PID_NO_L3;
  874.             bp->cnt = 1;
  875.             bp->next = qstring(tnc2->text[1]);
  876.             enqueue(&axp->txq,bp);
  877.         }
  878.         }
  879.  
  880.         if (tnc2->param[14])        /* CMSGDISC */
  881.         axp->r_upcall = axp->t_upcall = disc_ax25;
  882.         else
  883.         axp->r_upcall = tnc2_recv;
  884.  
  885.         if (tnc2->param[8])            /* CBELL */
  886.         tnc2_ochar(tnc2,'\007');
  887.     }
  888.  
  889.     if (tnc2->mode == TM_CMD && tnc2->linepos == 0 &&
  890.         !tnc2->param[47] && !tnc2->param[46]) /* NOMODE OFF, NEWMODE OFF */
  891.         tnc2->mode = tnc2->conmode;
  892.  
  893.     break;
  894.  
  895.     case DISCONNECTED:
  896.     if ((tnc2 = find_tnc(axp)) == NULLTNC)
  897.         return;
  898.  
  899.     tnc2->axp = NULLAX25;
  900.     break;
  901.     }
  902.  
  903.     if (msg != LAPBNOMS && msg != LAPBDISC &&
  904.     (tnc2->mode < TM_TRANS || msg < LAPBRESF)) {
  905.     sprintf(tmp,"*** %s: ",ax25mesgs[msg]);
  906.     tnc2_mesg(tnc2,tmp);
  907.     pdax25(tmp,&axp->addr);
  908.     tnc2_mesg(tnc2,tmp);
  909.  
  910.     if (msg == LAPBCONN && tnc2->param[22]){    /* CONSTAMP */
  911.         time_t tloc;
  912.  
  913.         time(&tloc);
  914.         sprintf(tmp," [%s]",tnc2_daytime(tnc2,tloc));
  915.         tnc2_mesg(tnc2,tmp);
  916.     }
  917.     tnc2_mesg(tnc2,tnc2_cr);
  918.     }
  919.  
  920.     if (new == DISCONNECTED) {
  921.     tnc2_mesg(tnc2,"*** DISCONNECTED\r");
  922.  
  923.     tnc2->status &= ~TS_DCD;            /* DCD off */
  924.  
  925.     if (tnc2->mode != TM_CMD &&
  926.         !tnc2->param[47] && tnc2->param[46]){ /* NOMODE OFF, NEWMODE ON */
  927.         tnc2->mode = TM_CMD;
  928.         tnc2->linepos = 0;
  929.         tnc2_flow(tnc2,2 - tnc2->param[72]);    /* flowctrl based on XFLOW */
  930.         tnc2_mesg(tnc2,tnc2_prompt);
  931.     }
  932.     }
  933. }
  934.  
  935. /* receive upcall handler for TNC2 */
  936. static void
  937. tnc2_recv (axp,cnt)
  938. struct ax25_cb *axp;
  939. int16 cnt;
  940.  
  941. {
  942.     register struct tnc *tnc2;
  943.     struct mbuf *bp,*recv_ax25();
  944.     unsigned char c;
  945.  
  946.     if (axp->rxq == NULLBUF)            /* nothing queued, */
  947.     return;                    /* nothing to do */
  948.  
  949.     if ((tnc2 = find_tnc(axp)) == NULLTNC) {
  950.     disc_ax25(axp);
  951.     return;
  952.     }
  953.  
  954.     switch (tnc2->mode)
  955.     {
  956.     case TM_CMD:                /* command mode */
  957.     return;
  958.  
  959.     case TM_CONV:                /* converse */
  960.     if (tnc2->param[29] && tnc2->linepos != 0) /* FLOW ON */
  961.         return;
  962.  
  963.     case TM_TRANS:                /* transparent */
  964.     if (!tnc2_ochar(tnc2,-1))        /* test for blocked queue */
  965.         return;
  966.  
  967.     if (tnc2->param[59] && (cnt == 0 || cnt > 255))
  968.         cnt = 255;                /* upper limit for RXBLOCK fmt */
  969.  
  970.     if ((bp = recv_ax25(axp,cnt)) == NULLBUF)
  971.         return;
  972.  
  973.     if (tnc2->param[59]) {            /* RXBLOCK */
  974.         tnc2_ochar(tnc2,0xff);
  975.         cnt = len_mbuf(bp);            /* bytes in received packet */
  976.         tnc2_ochar(tnc2,0xf0 | (cnt >> 4));
  977.         tnc2_ochar(tnc2,0xf0 | cnt);
  978.         tnc2_ochar(tnc2,PID_FIRST|PID_LAST|PID_NO_L3);
  979.     }
  980.  
  981.     while (pullup(&bp,&c,1) == 1) {
  982.         if (!tnc2_ochar(tnc2,c))
  983.         break;
  984.  
  985.         if (c == '\r' && tnc2->mode == TM_CONV && tnc2->param[2]) /* AUTOLF */
  986.         if (!tnc2_ochar(tnc2,'\n'))
  987.             break;
  988.     }
  989.  
  990.     free_p(bp);
  991.     break;
  992.     }
  993.  
  994. #ifdef BEACON
  995.     if (!tnc2->every[0])            /* BEACON AFTER */
  996.     start_timer(&tnc2->timer[0]);
  997. #endif
  998. }
  999.  
  1000. /* handle incoming packets in KISS mode */
  1001. /* when a TNC2 on this interface is in KISS mode, the packet is duplicated */
  1002. /* and sent to the client.  return value is 0 if no TNC found in KISS mode */
  1003. int
  1004. tnc2_kissrcv (interface,hdr,bp,kissrx)
  1005. struct interface *interface;
  1006. struct ax25 *hdr;
  1007. struct mbuf *bp;
  1008. int kissrx;
  1009.  
  1010. {
  1011.     register struct tnc *tnc2;
  1012.     struct mbuf *hbp,*dbp,*htonax25(),*slip_encode();
  1013.     int rv = 0;
  1014.     unsigned char c;
  1015.  
  1016.     for (tnc2 = tnc2s; tnc2 != NULLTNC; tnc2 = tnc2->next) {
  1017.     if (tnc2->interface == interface && tnc2->mode == TM_KISS) {
  1018.         /* only receive packets at or above KISSRX level */
  1019.         if (tnc2->param[77] >= kissrx) {        /* KISSRX */
  1020.         if (tnc2_ochar(tnc2,-1) &&        /* test for blocked queue */
  1021.             dup_p(&dbp,bp,0,(unsigned int) MAXINT16) != 0) {
  1022.             if ((hbp = htonax25(hdr,dbp)) != NULLBUF) {
  1023.             /* Put type field for KISS TNC on front */
  1024.             if ((dbp = pushdown(hbp,1)) != NULLBUF) {
  1025.                 hbp = NULLBUF;
  1026.                 dbp->data[0] = KISS_DATA;
  1027.                 dbp = slip_encode(dbp);
  1028.                 while (pullup(&dbp,&c,1) == 1)
  1029.                 if (!tnc2_ochar(tnc2,c))
  1030.                     break;
  1031.             }
  1032.             }
  1033.  
  1034.             /* when out of memory or fifo full, free everything */
  1035.             free_p(dbp);
  1036.             free_p(hbp);
  1037.         }
  1038.         }
  1039.  
  1040.         rv = 1;
  1041.     }
  1042.     }
  1043.  
  1044.     return rv;
  1045. }
  1046.  
  1047. #ifdef BEACON
  1048. /* send a beacon (arghh!!) */
  1049. static void
  1050. tnc2_beacon (tnc2)
  1051. register struct tnc *tnc2;
  1052.  
  1053. {
  1054.     struct ax25 beacon;
  1055.  
  1056.     if (tnc2->text[0] != NULLCHAR) {        /* BTEXT */
  1057.     memcpy(&beacon,&tnc2->unproto,sizeof(struct ax25));
  1058.     setcall(&beacon.dest,"BEACON");        /* send it to BEACON */
  1059.  
  1060.     sendui_ax25(tnc2->interface,&beacon,qstring(tnc2->text[0]),
  1061.             PID_FIRST|PID_LAST|PID_NO_L3);
  1062.     }
  1063.  
  1064.     if (tnc2->every[0])                /* BEACON EVERY */
  1065.     start_timer(&tnc2->timer[0]);
  1066. }
  1067. #endif
  1068.  
  1069. static void
  1070. dotnconmode (tnc2,args)
  1071. struct tnc *tnc2;
  1072. char *args;
  1073.  
  1074. {
  1075.     char *word;
  1076.  
  1077.     tnc2_mesg(tnc2,"CONMODE  ");
  1078.     if ((word = getword(&args)) != NULLCHAR)
  1079.     tnc2_mesg(tnc2,tnc2_was);
  1080.  
  1081.     tnc2_mesg(tnc2,(tnc2->conmode == TM_CONV)? "CONVERSE\r" : "TRANS\r");
  1082.  
  1083.     if (word != NULLCHAR)
  1084.     tnc2->conmode = (*word != 'T')? TM_CONV : TM_TRANS;
  1085. }
  1086.  
  1087. /* get callsign and digi string from commandline */
  1088. static int
  1089. tnc2_path (tnc2,addr,args)
  1090. register struct tnc *tnc2;
  1091. register struct ax25 *addr;            /* where to store it */
  1092. char **args;                    /* ptr to command tail ptr */
  1093.  
  1094. {
  1095.     register char *word;
  1096.  
  1097.     if ((word = getword(args)) == NULLCHAR)
  1098.     return 1;                /* nothing, return 1 */
  1099.  
  1100.     memset(addr,0,sizeof(*addr));
  1101.  
  1102.     if(setcall(&addr->dest,word) < 0){
  1103.     tnc2_mesg(tnc2,tnc2_call);
  1104.     return -1;
  1105.     }
  1106.  
  1107.     ASSIGN(addr->source,tnc2->axcall->addr);    /* Set sourcecall = our own call */
  1108.  
  1109.     if ((word = getword(args)) != NULLCHAR) {    /* Set digipeater path */
  1110.     if (strncmp(word,"VIA",strlen(word))) {
  1111.         tnc2_mesg(tnc2,"?VIA\r");
  1112.         return -1;
  1113.     }
  1114.  
  1115.     while ((word = getword(args)) != NULLCHAR) {
  1116.         if (++(addr->ndigis) > MAXDIGIS){
  1117.         tnc2_mesg(tnc2,tnc2_toomany);
  1118.         return -1;
  1119.         }
  1120.  
  1121.         if(setcall(&addr->digis[addr->ndigis - 1],word) < 0){
  1122.         tnc2_mesg(tnc2,tnc2_call);
  1123.         return -1;
  1124.         }
  1125.     }
  1126.     }
  1127.  
  1128.     return 0;                    /* all ok, return 0 */
  1129. }
  1130.  
  1131. static void
  1132. dotnconnect (tnc2,args)
  1133. struct tnc *tnc2;
  1134. char *args;
  1135.  
  1136. {
  1137.     struct ax25 addr;
  1138.     struct ax25_cb *open_ax25();
  1139.     int rv;
  1140.     extern int16 axwindow;
  1141.  
  1142.     if ((rv = tnc2_path(tnc2,&addr,&args)) < 0)
  1143.     return;
  1144.  
  1145.     if (rv != 0 || tnc2->axp != NULLAX25) {
  1146.     dotncstatus(tnc2);
  1147.     return;
  1148.     }
  1149.  
  1150.     if ((tnc2->axp = open_ax25(&addr,axwindow,tnc2_recv,NULLFP,tnc2_state,
  1151.                    tnc2->interface,(char *) tnc2->axcall)) == NULLAX25) {
  1152.     tnc2_mesg(tnc2,"*** FAILED\r");
  1153.     return;
  1154.     }
  1155.  
  1156.     if (!tnc2->param[47] && tnc2->param[46])    /* NOMODE OFF, NEWMODE ON */
  1157.     if ((tnc2->mode = tnc2->conmode) == TM_TRANS)
  1158.         dotntrans(tnc2);
  1159. }
  1160.  
  1161. static void
  1162. dotnconverse (tnc2)
  1163. struct tnc *tnc2;
  1164.  
  1165. {
  1166.     tnc2_flow(tnc2,2 - tnc2->param[72]);    /* flowctrl based on XFLOW */
  1167.     tnc2->mode = TM_CONV;
  1168. }
  1169.  
  1170. static void
  1171. dotncstatus (tnc2)
  1172. register struct tnc *tnc2;
  1173.  
  1174. {
  1175.     int state;
  1176.     char tmp[100];
  1177.  
  1178.     tnc2_mesg(tnc2,"Link state is: ");
  1179.  
  1180.     if (tnc2->axp == NULLAX25)
  1181.     state = DISCONNECTED;
  1182.     else
  1183.     state = tnc2->axp->state;
  1184.  
  1185.     switch (state)
  1186.     {
  1187.     case DISCONNECTED:
  1188.     tnc2_mesg(tnc2,"DISCONNECTED");
  1189.     break;
  1190.  
  1191.     case SETUP:
  1192.     tnc2_mesg(tnc2,"CONNECT in progress");
  1193.     break;
  1194.  
  1195.     case CONNECTED:
  1196.     tnc2_mesg(tnc2,"CONNECTED to ");
  1197.     pdax25(tmp,&tnc2->axp->addr);
  1198.     tnc2_mesg(tnc2,tmp);
  1199.     break;
  1200.  
  1201.     case DISCPENDING:
  1202.     tnc2_mesg(tnc2,"DISCONNECT in progress");
  1203.     break;
  1204.  
  1205.     case FRAMEREJECT:
  1206.     tnc2_mesg(tnc2,"FRMR in progress");
  1207.     break;
  1208.     }
  1209.  
  1210.     tnc2_mesg(tnc2,tnc2_cr);
  1211. }
  1212.  
  1213. static void
  1214. dotndaytime (tnc2,args)
  1215. struct tnc *tnc2;
  1216. char *args;
  1217.  
  1218. {
  1219.     time_t tloc;
  1220.  
  1221.     if (getword(&args) != NULLCHAR)        /* attempt to set clock */
  1222.     return;                    /* ignore it */
  1223.  
  1224.     time(&tloc);                /* read current time */
  1225.     tnc2_mesg(tnc2,tnc2_daytime(tnc2,tloc));    /* show current time */
  1226.     tnc2_mesg(tnc2,tnc2_cr);
  1227. }
  1228.  
  1229. static void
  1230. dotndiscon (tnc2)
  1231. struct tnc *tnc2;
  1232.  
  1233. {
  1234.     if (tnc2->axp == NULLAX25)
  1235.     dotncstatus(tnc2);
  1236.     else
  1237.     disc_ax25(tnc2->axp);
  1238. }
  1239.  
  1240. static void
  1241. dotndisplay (tnc2)
  1242. struct tnc *tnc2;
  1243.  
  1244. {
  1245.     register struct tnccommand *tcmd;
  1246.  
  1247.     for (tcmd = tnccommands; tcmd->name[0] != '\0'; tcmd++)
  1248.     if (tcmd->type != TC_EXEC)
  1249.         showparm(tnc2,tcmd,0);
  1250.     else
  1251.         if (tcmd->func == dotnmycall)
  1252.         dotnmycall(tnc2);
  1253. }
  1254.  
  1255. static void
  1256. dotnmycall (tnc2)
  1257. struct tnc *tnc2;
  1258.  
  1259. {
  1260.     char buf[10];
  1261.  
  1262.     tnc2_mesg(tnc2,"MYCALL   ");
  1263.     pax25(buf,&tnc2->axcall->addr);
  1264.     tnc2_mesg(tnc2,buf);
  1265.     tnc2_mesg(tnc2,tnc2_cr);
  1266. }
  1267.  
  1268. void dotnreset (tnc2)
  1269. struct tnc *tnc2;
  1270.  
  1271. {
  1272.     int i;
  1273.  
  1274.     for (i = 0; i < TNCTIMERS; i++)        /* stop timers */
  1275.     stop_timer(&tnc2->timer[i]);
  1276.  
  1277.     tnc2->conmode = TM_CONV;            /* CONMODE CONVERSE */
  1278.     memcpy(tnc2->param,def_params,sizeof(tnc2->param));
  1279.     for (i = 0; i < TNCTEXTS; i++) {        /* all texts empty */
  1280.     if (tnc2->text[i] != NULLCHAR)
  1281.         free(tnc2->text[i]);
  1282.     tnc2->text[i] = NULLCHAR;
  1283.     }
  1284.     tnc2->timer[0].start = 0;            /* BEACON EVERY 0 */
  1285. #ifdef BEACON
  1286.     tnc2->timer[0].func = tnc2_beacon;
  1287.     tnc2->timer[0].arg = (char *) tnc2;
  1288. #endif
  1289.     tnc2->every[0] = 1;
  1290.     tnc2->timer[1].start = SEC2TICK(1);        /* PACTIME AFTER 10 */
  1291.     tnc2->timer[1].func = tnc2_send;
  1292.     tnc2->timer[1].arg = (char *) tnc2;
  1293.     tnc2->every[1] = 0;
  1294.     memset(&tnc2->unproto,0,sizeof(tnc2->unproto));
  1295.     setcall(&tnc2->unproto.dest,"QST");        /* UNPROTO QST */
  1296.     ASSIGN(tnc2->unproto.source,tnc2->axcall->addr);
  1297.  
  1298.     dotnrestart(tnc2);
  1299. }
  1300.  
  1301. static void dotnrestart (tnc2)
  1302. struct tnc *tnc2;
  1303.  
  1304. {
  1305.     int i;
  1306.     extern char version[];
  1307.  
  1308.     for (i = 0; i < TNCTIMERS; i++)        /* stop timers */
  1309.     stop_timer(&tnc2->timer[i]);
  1310.  
  1311.     if (tnc2->axp != NULLAX25) {
  1312.     del_ax25(tnc2->axp);
  1313.     tnc2->axp = NULLAX25;
  1314.     }
  1315.  
  1316.     if (tnc2->param[76]) {            /* KISS */
  1317.     tnc2_flow(tnc2,0);            /* no flowctrl at all */
  1318.     tnc2->mode = TM_KISS;
  1319.  
  1320.     tnc2_mesg(tnc2,"[KISS]\300");        /* a hint for the user */
  1321.     return;
  1322.     }
  1323.  
  1324.     tnc2_flow(tnc2,2 - tnc2->param[72]);    /* flowctrl based on XFLOW */
  1325.  
  1326.     tnc2_mesg(tnc2,"\r\rAX.25 TNC2 emulator in KA9Q NET\rWritten by R.E. Janssen, PE1CHL\rRelease ");
  1327.     tnc2_mesg(tnc2,version);
  1328.     tnc2_mesg(tnc2,"\rInterface: ");
  1329.     tnc2_mesg(tnc2,tnc2->interface->name);
  1330.     tnc2_mesg(tnc2,tnc2_cr);
  1331. }
  1332.  
  1333. static void
  1334. dotntrans (tnc2)
  1335. struct tnc *tnc2;
  1336.  
  1337. {
  1338.     tnc2_flow(tnc2,2 - (tnc2->param[70] & tnc2->param[72])); /* TXFLOW and XFLOW */
  1339.     tnc2->mode = TM_TRANS;
  1340. }
  1341.  
  1342. static void
  1343. dotnunproto (tnc2,args)
  1344. struct tnc *tnc2;
  1345. char *args;
  1346.  
  1347. {
  1348.     struct ax25 addr;
  1349.     int rv;
  1350.     char buf[100];
  1351.  
  1352.     if ((rv = tnc2_path(tnc2,&addr,&args)) < 0)
  1353.     return;
  1354.  
  1355.     tnc2_mesg(tnc2,"UNPROTO  ");
  1356.     if (!rv)
  1357.     tnc2_mesg(tnc2,tnc2_was);
  1358.  
  1359.     pdax25(buf,&tnc2->unproto);
  1360.     tnc2_mesg(tnc2,buf);
  1361.     tnc2_mesg(tnc2,tnc2_cr);
  1362.  
  1363.     if (!rv)
  1364.     memcpy(&tnc2->unproto,&addr,sizeof(addr));
  1365. }
  1366.